#include <milfs_keyvalue.h>
#include <milfs_db.h>
#include <tcutil.h>
#include <tchdb.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <time.h>

TCHDB *__mlf_db = NULL;
char *__mlf_db_path = NULL;

/* private method prototypes */
bool __mlf_obtain_lock();
int __mlf_open_db_write();
int __mlf_open_db_read();
int __mlf_close_db();


int mlf_db_init(char *path)
{
  int ecode = 0;

  int path_size = strlen(path) + sizeof(char);
  __mlf_db_path = (char *) malloc( path_size );
  strncpy( __mlf_db_path, path, path_size );

  /* create the object */
  __mlf_db = tchdbnew();

  if ( __mlf_db != NULL ) {
    ecode = 1;

    if ( tcrealpath( __mlf_db_path ) == NULL ) {
      ecode = __mlf_open_db_write();
    }
  }

  return ecode;
}

int mlf_db_shutdown()
{
  int ret_val;

  if ( __mlf_obtain_lock() ) {
    free( __mlf_db_path );
    tchdbdel( __mlf_db );

    __mlf_db      = NULL;
    __mlf_db_path = NULL;

    ret_val = 1;
  } else {
    ret_val = 0;
  }

  return ret_val;
}

bool mlf_db_key_exist(mlf_key_value *kv)
{
  bool truth = false;

  if ( __mlf_db_open_read() ) {
    if ( tchdbvsiz(__mlf_db, kv->key, kv->key_size) != -1 ) {
      truth = true;
    }
  }

  return truth;
}

int mlf_db_write(mlf_key_value *kv)
{
  int ecode = 0;

  ecode = __mlf_db_open_write();
  if ( ecode > 0 ) {
    ecode = tchdbput(__mlf_db, kv->key, kv->key_size, kv->value, kv->value_size);
    ecode = ecode && __mlf_db_close();
  }

  return ecode;
}

int milf_db_read(mlf_key_value *kv)
{
  int ecode = 0;

  if ( __mlf_db_open_read() ) {
    kv->value = (char *) tchdbget(__mlf_db, kv->key, kv->key_size, &kv->value_size);

    if ( kv->value != NULL ) {
      ecode = 1;
    }
  }

  return ecode;
}

int mlf_db_delete(mlf_key_value *kv)
{
  int ecode = 0;

  ecode = __mlf_db_open_write();
  if ( ecode > 0 ) {
    ecode = tchdbout(__mlf_db, kv->key, kv->key_size);
    ecode = ecode && __mlf_db_close();
  }

  return ecode;
}


/* private methods */

bool __mlf_obtain_lock()
{
  bool truth = false;
  
  while ( true ) {
    if( __mlf_db != NULL ) {
      truth = tchdbsetmutex( __mlf_db );
      if ( truth ) {
	break;
      } else {
	sleep(1);
      }
    } else {
      truth = false;
      break;
    }
  }

  return truth;
}

int __mlf_open_db_write()
{
  int ecode = 0;

  if ( __mlf_obtain_lock ) {
    /* open the database */
    if (! tchdbopen(__mlf_db, __mlf_db_path, HDBOWRITER | HDBOCREAT | HDBOLCKNB)) {
      ecode = tchdbecode(__mlf_db);
      fprintf(stderr, "open error: %s\n", tchdberrmsg(ecode));
    } else {
      /* tune the database */
      if (! tchdboptimize(__mlf_db, -1, -1, -1, HDBTLARGE)) {
	ecode = tchdbecode(__mlf_db);
	fprintf(stderr, "optimize error: %s\n", tchdberrmsg(ecode));
      } else {
	ecode = tchdbecode(__mlf_db);
      }
    }
  }

  return ecode;
}

int __mlf_open_db_read()
{
  int ecode = 0;

  if ( __mlf_db != NULL ) {
    /* open the database */
    if (! tchdbopen(__mlf_db, __mlf_db_path, HDBOREADER | HDBOLCKNB)) {
      ecode = tchdbecode(__mlf_db);
      fprintf(stderr, "open error: %s\n", tchdberrmsg(ecode));
    } else {
      ecode = tchdbecode(__mlf_db);
    }
  }

  return ecode;
}

int __mlf_close_db()
{
  int ecode = 0;

  if ( __mlf_db != NULL ) {
    /* close the database */
    ecode = tchdbclose(__mlf_db);
  }

  return ecode;
}

